package me.zhengjie.modules.web.service.order.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import me.zhengjie.config.entity.PageData;
import me.zhengjie.constant.ConstantUtils;
import me.zhengjie.modules.system.domain.*;
import me.zhengjie.modules.system.service.*;
import me.zhengjie.modules.system.service.dto.*;
import me.zhengjie.modules.system.service.mapstruct.*;
import me.zhengjie.modules.web.domain.order.CommonPageReq;
import me.zhengjie.modules.web.domain.order.OrderListRequest;
import me.zhengjie.modules.web.domain.order.OrderListResponse;
import me.zhengjie.modules.web.domain.order.WholeBaseResponse;
import me.zhengjie.modules.web.domain.whole.WholeClassResponse;
import me.zhengjie.modules.web.domain.whole.WholeCourseResponse;
import me.zhengjie.modules.web.domain.whole.WholeReportResponse;
import me.zhengjie.modules.web.enums.OrderStatusEnum;
import me.zhengjie.modules.web.service.mybatisplus.ICourseApplicantsPlusService;
import me.zhengjie.modules.web.service.order.IOrderService;
import me.zhengjie.request.ATMRContext;
import me.zhengjie.utils.DateUtil;
import me.zhengjie.utils.RedisUtils;
import me.zhengjie.utils.enums.OrderTypeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

@Service
public class OrderServiceImpl implements IOrderService {

    @Autowired
    private CourseOrderExpMapper courseOrderExpMapper;

    @Autowired
    private CourseMealsOrderExpMapper courseMealsOrderExpMapper;

    @Autowired
    private IdentityOrderExpMapper identityOrderExpMapper;

    @Autowired
    private ExamOrderExpMapper examOrderExpMapper;

    @Autowired
    private CourseClassStudentExpMapper courseClassStudentExpMapper;

    @Autowired
    private ExamResultExpMapper examResultExpMapper;

    @Autowired
    private UserChatOrderPlusMapper chatOrderPlusMapper;

    @Autowired
    private CourseInfoService courseInfoService;

    @Autowired
    private AtmrService atmrService;

    @Autowired
    private CourseClassService courseClassService;

    @Autowired
    private IdentityItemService identityItemService;

    @Autowired
    private CourseClockService courseClockService;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private ICourseApplicantsPlusService applicantsPlusService;

    /**
     * 课程订单/测评订单/餐费订单/身份订单
     * 1234
     * @param request
     * @return
     */
    @Override
    public PageData<OrderListResponse> list(OrderListRequest request) {
        String openId = ATMRContext.getContext().getOpenId();
        switch (request.getOrderType()){
            case 1:
                Page<CourseOrder> page = Page.of(request.getPage(), request.getSize());
                LambdaQueryWrapper<CourseOrder> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(CourseOrder::getOpenId,openId);
                Integer orderStatus = request.getOrderStatus();
                if (Objects.nonNull(orderStatus) && orderStatus.equals(OrderStatusEnum.Cancel.getOrderStatus())){
                    wrapper.in(CourseOrder::getOrderStatus, Lists.newArrayList(OrderStatusEnum.Cancel.getOrderStatus(), OrderStatusEnum.Refund.getOrderStatus()));
                }else {
                    wrapper.eq(Objects.nonNull(request.getOrderStatus()),CourseOrder::getOrderStatus,request.getOrderStatus());
                }
                wrapper.orderByDesc(CourseOrder::getCreateTime);
                Page<CourseOrder> courseOrderPage = courseOrderExpMapper.selectPage(page, wrapper);
                return PageData.valueOf(formatCourseOrderData(courseOrderPage.getRecords()),courseOrderPage.getTotal(),courseOrderPage.getPages());
            case 2:
                Page<ExamOrder> examPage = Page.of(request.getPage(), request.getSize());
                LambdaQueryWrapper<ExamOrder> examwrapper = new LambdaQueryWrapper<>();
                examwrapper.eq(Objects.nonNull(request.getOrderStatus()),ExamOrder::getOrderStatus,request.getOrderStatus()).eq(ExamOrder::getOpenId,openId);
                examwrapper.orderByDesc(ExamOrder::getCreateTime);
                Page<ExamOrder> examOrderPage = examOrderExpMapper.selectPage(examPage, examwrapper);
                return PageData.valueOf(formatExamOrderData(examOrderPage.getRecords()),examOrderPage.getTotal(),examOrderPage.getPages());
            case 3:
                Page<CourseMealsOrder> mealPage = Page.of(request.getPage(), request.getSize());
                LambdaQueryWrapper<CourseMealsOrder> mealWrapper = new LambdaQueryWrapper<>();
                mealWrapper.eq(Objects.nonNull(request.getOrderStatus()),CourseMealsOrder::getOrderStatus,request.getOrderStatus()).eq(CourseMealsOrder::getOpenId,openId);
                mealWrapper.orderByDesc(CourseMealsOrder::getCreateTime);
                Page<CourseMealsOrder> courseMealsOrderPage = courseMealsOrderExpMapper.selectPage(mealPage, mealWrapper);
                return PageData.valueOf(formatMealsOrderData(courseMealsOrderPage.getRecords()),courseMealsOrderPage.getTotal(),courseMealsOrderPage.getPages());

            case 4:
                Page<IdentityOrder> identityPage = Page.of(request.getPage(), request.getSize());
                LambdaQueryWrapper<IdentityOrder> identityWrapper = new LambdaQueryWrapper<>();
                identityWrapper.eq(Objects.nonNull(request.getOrderStatus()),IdentityOrder::getOrderStatus,request.getOrderStatus()).eq(IdentityOrder::getOpenId,openId);
                identityWrapper.orderByDesc(IdentityOrder::getCreateTime);
                Page<IdentityOrder> identityOrderPage = identityOrderExpMapper.selectPage(identityPage, identityWrapper);
                return PageData.valueOf(formatIdentityOrderData(identityOrderPage.getRecords()),identityOrderPage.getTotal(),identityOrderPage.getPages());

            case 5:
                Page<UserChatOrder> chatOrderPage = Page.of(request.getPage(), request.getSize());
                LambdaQueryWrapper<UserChatOrder> chatOrderWrapper = new LambdaQueryWrapper<>();
                chatOrderWrapper.eq(Objects.nonNull(request.getOrderStatus()),UserChatOrder::getOrderStatus,request.getOrderStatus()).eq(UserChatOrder::getOpenId,openId);
                chatOrderWrapper.orderByDesc(UserChatOrder::getCreateTime);
                Page<UserChatOrder> userChatOrderPage = chatOrderPlusMapper.selectPage(chatOrderPage, chatOrderWrapper);
                return PageData.valueOf(formatUserChatOrderData(userChatOrderPage.getRecords()),userChatOrderPage.getTotal(),userChatOrderPage.getPages());
        }
        return null;
    }

    @Override
    public PageData<WholeCourseResponse> course(CommonPageReq request) {
        Page<CourseOrder> page = Page.of(request.getPage(), request.getSize());
        LambdaQueryWrapper<CourseOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseOrder::getPayStatus,ConstantUtils.Order_Payed).eq(CourseOrder::getOpenId,ATMRContext.getContext().getOpenId());
        wrapper.orderByDesc(CourseOrder::getCreateTime);
        Page<CourseOrder> courseOrderPage = courseOrderExpMapper.selectPage(page, wrapper);
        return PageData.valueOf(formatCourseData(courseOrderPage.getRecords()),courseOrderPage.getTotal(),courseOrderPage.getPages());
    }

    @Override
    public PageData<WholeClassResponse> classes(CommonPageReq request) {
        Page<CourseClassStudent> page = Page.of(request.getPage(), request.getSize());
        LambdaQueryWrapper<CourseClassStudent> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseClassStudent::getOpenId,ATMRContext.getContext().getOpenId());
        wrapper.orderByDesc(CourseClassStudent::getCreateTime);
        Page<CourseClassStudent> courseOrderPage = courseClassStudentExpMapper.selectPage(page, wrapper);
        return PageData.valueOf(formatClassesData(courseOrderPage.getRecords()),courseOrderPage.getTotal(),courseOrderPage.getPages());
    }

    @Override
    public PageData<WholeReportResponse> report(CommonPageReq request) {
        Page<ExamResult> page = Page.of(request.getPage(), request.getSize());
        LambdaQueryWrapper<ExamResult> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ExamResult::getWxId,ATMRContext.getContext().getOpenId());
        wrapper.orderByDesc(ExamResult::getExamTime);
        Page<ExamResult> courseOrderPage = examResultExpMapper.selectPage(page, wrapper);
        return PageData.valueOf(formatReportData(courseOrderPage.getRecords()),courseOrderPage.getTotal(),courseOrderPage.getPages());
    }

    @Override
    public boolean orderExpire2Redis(Integer orderType, String orderSn, Timestamp expireTime) {
        try {
            return redisUtils.hset(ConstantUtils.DefaultTimeRedisKey+"_"+orderType,orderType + "_"+orderSn,expireTime);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private List<WholeReportResponse> formatReportData(List<ExamResult> list) {
        List<WholeReportResponse> responseList = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)){
            return responseList;
        }
        for (ExamResult courseOrder : list) {
            WholeReportResponse resp = new WholeReportResponse();
            resp.setCreateTime(courseOrder.getExamTime());
            resp.setBusinessNo(String.valueOf(courseOrder.getId()));
            resp.setAtmrId(courseOrder.getAtmrId());
            AtmrDto infoByNo = atmrService.findById(Integer.valueOf(courseOrder.getAtmrId()));
            if (Objects.nonNull(infoByNo)){
                resp.setPic(infoByNo.getIconUrl());
                resp.setName(infoByNo.getAtmrName());
                resp.setPrice(new BigDecimal(infoByNo.getPrice()));
                resp.setAgeType(infoByNo.getAgeType());
                resp.setAtmrType(infoByNo.getAtmrType());
            }
            responseList.add(resp);
        }
        return responseList;
    }

    private List<WholeClassResponse> formatClassesData(List<CourseClassStudent> list) {
        List<WholeClassResponse> responseList = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)){
            return responseList;
        }
        String openId = ATMRContext.getContext().getOpenId();
        for (CourseClassStudent courseOrder : list) {
            WholeClassResponse resp = new WholeClassResponse();
            resp.setCreateTime(DateUtil.getTimeStamp(courseOrder.getCreateTime()));
            resp.setBusinessNo(courseOrder.getClassNo());
            CourseClass classByCourseNo = courseClassService.getClassByCourseNo(courseOrder.getCourseNo());
            if (Objects.nonNull(classByCourseNo)){
                resp.setName(classByCourseNo.getName());
            }
            CourseInfo infoByNo = courseInfoService.getInfoByNo(courseOrder.getCourseNo());
            if (Objects.nonNull(infoByNo)){
                resp.setPic(infoByNo.getCourseLogo());
                resp.setPrice(infoByNo.getSellPrice());
                resp.setCourseNo(infoByNo.getCourseNo());
                resp.setCourseName(infoByNo.getCourseName());
                resp.setCourseCover(infoByNo.getCourseLogo());
            }
            CourseClockQueryCriteria clockQueryCriteria = new CourseClockQueryCriteria();
            clockQueryCriteria.setCourseNo(courseOrder.getCourseNo());
            List<CourseClockDto> courseClockDtos = courseClockService.queryAll(clockQueryCriteria);
            if (CollectionUtils.isNotEmpty(courseClockDtos)) {
                resp.setHomeworkNumber(courseClockDtos.size());
                long collect = courseClockDtos.parallelStream().filter(distinctByKey(CourseClockDto::getOpenId)).count();
                resp.setPeopleNumber((int) collect);
                long count = courseClockDtos.parallelStream().filter(p -> p.getOpenId().equals(openId)).count();
                resp.setCompleteDays((int) count);
            }
            responseList.add(resp);
        }
        return responseList;
    }

    private List<WholeCourseResponse> formatCourseData(List<CourseOrder> list) {
        List<WholeCourseResponse> responseList = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)){
            return responseList;
        }
        for (CourseOrder courseOrder : list) {
            WholeCourseResponse resp = new WholeCourseResponse();
            resp.setCreateTime(DateUtil.getTimeStamp(courseOrder.getCreateTime()));
            resp.setBusinessNo(courseOrder.getCourseNo());
            resp.setPrice(courseOrder.getOrderAmount());
            resp.setCourseNo(courseOrder.getCourseNo());
            CourseInfo infoByNo = courseInfoService.getInfoByNo(courseOrder.getCourseNo());
            if (Objects.nonNull(infoByNo)){
                resp.setPic(infoByNo.getCourseLogo());
                resp.setName(infoByNo.getCourseName());
                resp.setSubTitle(infoByNo.getSubTitle());
                Timestamp openTime = infoByNo.getOpenTime();
                if (openTime.before(DateUtil.TimestampNow())) {
                    resp.setOpenStatus(1);
                }
            }
            int numbers = 1;
            resp.setApplicantsList(new ArrayList<>());
            LambdaQueryWrapper<CourseApplicants> applicantsWrapper = new LambdaQueryWrapper<>();
            applicantsWrapper.eq(CourseApplicants::getOrderNo,courseOrder.getOrderNo());
            List<CourseApplicants> applicantsList = applicantsPlusService.list(applicantsWrapper);
            if (CollectionUtil.isNotEmpty(applicantsList)){
                numbers = applicantsList.size();
                resp.setApplicantsList(applicantsList);
            }
            resp.setNumbers(numbers);
            responseList.add(resp);
        }
        return responseList;
    }
    private List<OrderListResponse> formatIdentityOrderData(List<IdentityOrder> list) {
        List<OrderListResponse> responseList = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)){
            return responseList;
        }
        for (IdentityOrder courseOrder : list) {
            OrderListResponse resp = new OrderListResponse();
            resp.setCreateTime(DateUtil.getTimeStamp(courseOrder.getCreateTime()));
            resp.setOrderSn(courseOrder.getOrderNo());
            resp.setOrderAmount(courseOrder.getIdentityAmount());
            resp.setPayoff(courseOrder.getPayStatus().equals(ConstantUtils.Order_Payed));
            resp.setOrderType(OrderTypeEnum.Identity.getOrderType());
            resp.setOrderTypeDesc(OrderTypeEnum.Identity.getDesc());
            resp.setOrderStatus(courseOrder.getOrderStatus());
            IdentityItem one = identityItemService.findOne(courseOrder.getBizId());
            if (Objects.nonNull(one)){
                resp.setItemPic(one.getItemPic());
                resp.setItemTitle(one.getTitle());
            }
            responseList.add(resp);
        }
        return responseList;
    }

    private List<OrderListResponse> formatMealsOrderData(List<CourseMealsOrder> list) {
        List<OrderListResponse> responseList = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)){
            return responseList;
        }
        for (CourseMealsOrder courseOrder : list) {
            OrderListResponse resp = new OrderListResponse();
            resp.setCreateTime(DateUtil.getTimeStamp(courseOrder.getCreateTime()));
            resp.setOrderSn(courseOrder.getOrderNo());
            resp.setOrderAmount(courseOrder.getMeals());
            resp.setPayoff(courseOrder.getPayStatus().equals(ConstantUtils.Order_Payed));
            resp.setOrderType(OrderTypeEnum.Meals.getOrderType());
            resp.setOrderTypeDesc(OrderTypeEnum.Meals.getDesc());
            resp.setOrderStatus(courseOrder.getOrderStatus());
            CourseClassQueryCriteria courseClassQueryCriteria = new CourseClassQueryCriteria();
            courseClassQueryCriteria.setClassNo(courseOrder.getClassNo());
            List<CourseClassDto> infoByNo = courseClassService.queryAll(courseClassQueryCriteria);
            if (CollectionUtil.isNotEmpty(infoByNo)){
                CourseClassDto courseClassDto = infoByNo.get(0);
                CourseInfo courseInfo = courseInfoService.getInfoByNo(courseClassDto.getCourseNo());
                if (Objects.nonNull(courseInfo)){
                    resp.setItemPic(courseInfo.getCourseLogo());
                    resp.setItemTitle(courseInfo.getCourseName()+"餐费");
                }
            }
            responseList.add(resp);
        }
        return responseList;
    }

    private List<OrderListResponse> formatExamOrderData(List<ExamOrder> list) {
        List<OrderListResponse> responseList = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)){
            return responseList;
        }
        for (ExamOrder courseOrder : list) {
            OrderListResponse resp = new OrderListResponse();
            resp.setCreateTime(DateUtil.getTimeStamp(courseOrder.getCreateTime()));
            resp.setOrderSn(courseOrder.getOrderNo());
            resp.setOrderAmount(courseOrder.getOrderAmount());
            resp.setPayoff(courseOrder.getPayStatus().equals(ConstantUtils.Order_Payed));
            resp.setOrderType(OrderTypeEnum.Exam.getOrderType());
            resp.setOrderTypeDesc(OrderTypeEnum.Exam.getDesc());
            resp.setOrderStatus(courseOrder.getOrderStatus());
            AtmrDto infoByNo = atmrService.findById(Integer.valueOf(courseOrder.getAtmrId()));
            if (Objects.nonNull(infoByNo)){
                resp.setItemPic(infoByNo.getIconUrl());
                resp.setItemTitle(infoByNo.getAtmrName());
            }
            responseList.add(resp);
        }
        return responseList;
    }

    private List<OrderListResponse> formatCourseOrderData(List<CourseOrder> list) {
        List<OrderListResponse> responseList = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)){
            return responseList;
        }
        for (CourseOrder courseOrder : list) {
            OrderListResponse resp = new OrderListResponse();
            Integer numbers = 1;
            if (courseOrder.getPayStatus().equals(ConstantUtils.Order_Payed)){
                LambdaQueryWrapper<CourseApplicants> applicantsWrapper = new LambdaQueryWrapper<>();
                applicantsWrapper.eq(CourseApplicants::getOrderNo,courseOrder.getOrderNo());
                List<CourseApplicants> applicantsList = applicantsPlusService.list(applicantsWrapper);
                if (CollectionUtil.isNotEmpty(applicantsList)){
                    numbers = applicantsList.size();
                    resp.setApplicantsList(applicantsList);
                }
            }
            resp.setNumbers(numbers);
            resp.setCreateTime(DateUtil.getTimeStamp(courseOrder.getCreateTime()));
            resp.setOrderAmount(courseOrder.getOrderAmount());
            resp.setPayoff(courseOrder.getPayStatus().equals(ConstantUtils.Order_Payed));
            resp.setItemTitle(courseOrder.getCourseName());
            resp.setOrderSn(courseOrder.getOrderNo());
            resp.setOrderStatus(courseOrder.getOrderStatus());
            resp.setOrderType(OrderTypeEnum.Course.getOrderType());
            resp.setOrderTypeDesc(OrderTypeEnum.Course.getDesc());
            CourseInfo infoByNo = courseInfoService.getInfoByNo(courseOrder.getCourseNo());
            if (Objects.nonNull(infoByNo)){
                resp.setItemPic(infoByNo.getCourseLogo());
            }
            responseList.add(resp);
        }
        return responseList;
    }


    private List<OrderListResponse> formatUserChatOrderData(List<UserChatOrder> list) {
        List<OrderListResponse> responseList = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)){
            return responseList;
        }
        for (UserChatOrder userChatOrder : list) {
            OrderListResponse resp = new OrderListResponse();
            resp.setCreateTime(DateUtil.getTimeStamp(userChatOrder.getCreateTime()));
            resp.setOrderSn(userChatOrder.getOrderNo());
            resp.setOrderAmount(userChatOrder.getChatAmount());
            resp.setPayoff(userChatOrder.getPayStatus().equals(ConstantUtils.Order_Payed));
            resp.setOrderType(OrderTypeEnum.Chat.getOrderType());
            resp.setOrderTypeDesc(OrderTypeEnum.Chat.getDesc());
            resp.setOrderStatus(userChatOrder.getOrderStatus());
            String settingKey = userChatOrder.getSettingKey();
            String name = "";
            if ("Month_1".equals(settingKey)){
                name = "30天会员";
            }else if ("Month_3".equals(settingKey)){
                name = "3个月会员";
            }else if ("Month_6".equals(settingKey)){
                name = "半年会员";
            }else if ("Month_12".equals(settingKey)){
                name = "一年会员";
            }
            resp.setItemPic("");
            resp.setItemTitle(name);
            responseList.add(resp);
        }
        return responseList;
    }
    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return object -> seen.putIfAbsent(keyExtractor.apply(object), Boolean.TRUE) == null;
    }
}
